/**
 * Implementacion de la clase ObjectValidator.
 */

#include <stdlib.h>
#include "../list/listIterator.hpp"
#include "../config/config.hpp"
#include "../main/Object.hpp"
#include "objectValidator.hpp"

void ObjectValidator::create(std::string &fileToParse, Logger *log, Graph *graph)
{
	this->setParser(fileToParse,log);
	this->logger = log;
	int number = atoi(Config::get("number_of_fields_expected_mapobject").c_str());
	if(number > 0)
	{
		this->numberOfFieldsExpected = number;
		this->mapObject.setSuccessStatus(5);
	}
	else
	{
		std::string detail = "Cantidad de campos esperados para la validacion de los objetos de mapa no tiene el formato esperado";
		this->logger->reportErrorMessage("ObjectValidator","create",detail);
		this->mapObject.setFailStatus(5);
	}
	this->graph = graph;
	
}

ObjectValidator::ObjectValidator(std::string &file_to_parse, Logger *log, Graph *graph)
{
	this->create(file_to_parse,log,graph);
}

ObjectValidator::~ObjectValidator()
{
	this->logger = NULL;
	this->numberOfFieldsExpected = 0;
}

void ObjectValidator::validateNumberOfFields(int lineCount, int fieldAmount)
{
	if(fieldAmount != this->numberOfFieldsExpected)
	{
		this->mapObject.setFailStatus(5);
		std::string detail = "Cantidad de campos distinta a la esperada";
		this->logger->reportErrorMessage("ObjectValidator","validateNumberOfFields", detail, lineCount);
	}
	else
		this->mapObject.setSuccessStatus(5);
}

void ObjectValidator::validateField(int lineCount, int fieldNumber, std::string field)
{
	std::string detail;
	if(fieldNumber <= 2 )
	{
		if((std::string::npos == field.find_first_of(','))&&(std::string::npos == field.find_first_of('.'))
			&&(field[0] != '0')&&(atoi(field.c_str()) == 0))
		{
			if(fieldNumber == 1)
				this->mapObject.setMapObjectId(field);
			else if (fieldNumber == 2)
				this->mapObject.setEdgeId(field);
		}
		else
		{
			detail = "Esperado un literal, se encontro un real";
			this->logger->reportErrorMessage("ObjectValidator","validateField", detail,lineCount,fieldNumber);
			this->mapObject.setFailStatus(fieldNumber);
		}
	}
	else if(fieldNumber == 3)
	{
		int numero = atoi(field.c_str());
		if(numero > 0)
		{
			this->mapObject.setHeight(numero);
		}
		else if (numero <= 0)
		{
			detail = "Esperado un real, se encontro un literal, o el real vale cero";
			this->logger->reportWarningMessage("ObjectValidator","validateField", detail,lineCount,fieldNumber);
			this->mapObject.setFailStatus(fieldNumber);
		}
	}
	else if(fieldNumber == 4)
	{
		if(strcmp("hotel",field.c_str()) == 0)
			this->mapObject.setType(HOTEL);
		else if (strcmp("restaurant",field.c_str()) == 0)
			this->mapObject.setType(RESTAURANT);
		else if (strcmp("comisaria",field.c_str()) == 0)
			this->mapObject.setType(COMISARIA);
		else if (strcmp("escuela",field.c_str()) == 0)
			this->mapObject.setType(ESCUELA);
		else if (strcmp("hospital",field.c_str()) == 0)
			this->mapObject.setType(HOSPITAL);
		else
		{
			detail = "Se encontro un tipo de objeto de mapa no esperado";
			this->logger->reportErrorMessage("ObjectValidator","validateField", detail,lineCount,fieldNumber);
			this->mapObject.setFailStatus(fieldNumber);
		}
	}	
}

void ObjectValidator::validateElementAdding(int lineCount)
{
	bool success = true;
	std::string detail;

	if(this->mapObject.getMapObjectIdStatus() == 0)
		success = false;
	if(this->mapObject.getEdgeIdStatus() == 0)
		success = false;
	if(this->mapObject.getHeightStatus() == 0)
		success = false;
	if(this->mapObject.getNumberOfFieldsStatus() == 0)
		success = false;

	if(success)
	{
		Object *moTemp = this->graph->getObject(this->mapObject.getMapObjectId());
		Edge* eTemp = this->graph->getEdge(this->mapObject.getEdgeId());
		if((moTemp == NULL)&&(eTemp != NULL))
		{
			moTemp = new Object(this->mapObject.getMapObjectId(),this->mapObject.getEdgeId(),
			                       this->mapObject.getHeight(),this->mapObject.getType());
			bool samePosition = false;

			List<Object> *lista = this->graph->getObjects();
			ListIterator<Object> it = lista->iterator();
			while((it.hasNext())&&(!samePosition))
			{
				if(((Object*)it.next())->hasSamePosition(moTemp))
					samePosition = true;
			}

			if(!samePosition)
			{			
				this->graph->addObject(moTemp);
				detail = "Objeto de mapa agregado, id: "; detail += this->mapObject.getMapObjectId();
				this->logger->reportSuccessMessage("ObjectValidator","validateGraphElementAdding",detail);
			}
			else
			{
				detail = "Objeto de mapa con la misma altura y arista existente, nuevo nodo descartado, id: ";
				detail += this->mapObject.getMapObjectId();
				this->logger->reportErrorMessage("ObjectValidator","validateGraphElementAdding",detail,lineCount);
			}
		}
		else
		{
			if(moTemp != NULL)
			{
				detail = "Objeto de mapa con Id existente, objeto descartado, id: "; detail += this->mapObject.getMapObjectId();
			}
			else if(eTemp == NULL)
			{
				detail = "Objeto de mapa con Id de arista inexistente, objeto descartado, id: "; detail += this->mapObject.getMapObjectId();
			}

			this->logger->reportErrorMessage("ObjectValidator","validateGraphElementAdding",detail,lineCount);
		}
		
	}
	else
	{
		detail = "Objecto de mapa descartado";
		this->logger->reportErrorMessage("ObjectValidator","validateGraphElementAdding",detail);
		this->mapObject.clear();
	}

}